# -*- coding: utf-8 -*-
# @Version: Python 3.9
# @Time    : 2023/2/2 16:05
# @Author  : chenyinhua
# @File    : conftest.py
# @Software: PyCharm
# @Desc: 这是文件的描述信息

# 标准库导入
import time
import os
from datetime import datetime
# 第三方库导入
from loguru import logger
import pytest
# 本地应用/模块导入
from config.path_config import IMG_DIR, REPORT_DIR
from config.settings import RunConfig
from config.global_vars import GLOBAL_VARS
from case_utils.allure_handle import allure_step, allure_title
from case_utils.data_handle import data_handle


@pytest.fixture(scope="session")
def browser_context_args(browser_context_args):
    return {
        **browser_context_args,
        "viewport": RunConfig.window_size
    }


# ------------------------------------- START: pytest-bdd钩子函数处理---------------------------------------#
def pytest_bdd_apply_tag(tag, function):
    """
    自定义标记转换为 pytest 标记的行为
    """
    if tag == 'todo':
        marker = pytest.mark.skip(reason="还没有处理完成~")
        marker(function)
        return True
    else:
        return None


def pytest_bdd_step_error(request, feature, scenario, step, step_func, step_func_args, exception):
    """
    当步骤函数执行失败时调用。可以在此处理步骤失败的情况。它接收 request 对象、feature 对象、scenario 对象、step 对象、step_func 函数、step_func_args 参数和 exception 异常对象作为参数。
    :param request: 包含有关测试请求的信息。
    :param feature: 当前特性的元数据。
    :param scenario: 当前场景的元数据。
    :param step: 当前步骤的元数据。
    :param step_func: 当前步骤的函数对象。
    :param step_func_args: 当前步骤的函数参数。
    :param exception: 捕获到的异常对象。
    :return:
    """

    feature_name = getattr(feature, 'name', None)
    feature_description = getattr(feature, 'description', None)

    scenario_name = getattr(scenario, 'name', None)

    step_name = getattr(step, "name", None)

    logger.error("\n" + "=" * 80
                 + "\n-------------测试步骤出错了--------------------\n"
                   f"Feature: {feature_name}\n"
                   f"Scenario: {scenario_name}\n"
                   f"Step: {step}\n"
                   f"Exception:   {exception}\n"
                 + "=" * 80)
    print("\n" + "=" * 80
          + "\n-------------测试步骤出错了--------------------\n"
            f"Feature: {feature_name}\n"
            f"Scenario: {scenario_name}\n"
            f"Step: {step}\n"
            f"Exception:   {exception}\n"
          + "=" * 80)

    allure_step(step_title=f"{step}: 测试出错了！")

    # 堆栈跟踪
    logger.error(f"Stack trace: {exception.__traceback__}\n")


def pytest_bdd_before_scenario(request, feature, scenario):
    """
    在执行场景之前调用。可以在此钩子中执行任何预处理操作。它接收 request 对象、feature 对象和 scenario 对象作为参数。
    :param request:
    :param feature:
    :param scenario:
    :return:
    """
    feature_name = getattr(feature, "name", None)
    scenario_name = getattr(scenario, "name", None)
    allure_title(title=f"{feature_name}-{scenario_name}")


def pytest_bdd_before_step(request, feature, scenario, step, step_func):
    """在执行步骤函数并评估其参数之前调用"""

    step_name = getattr(step, "name", None)

    # 对步骤进行处理，将需要替换的关键字${},利用data_handle方法进行替换处理
    setattr(step, "lines", [])
    logger.debug(f"\nSTEP BEFORE：{step_name}\n")
    new_step_name = data_handle(step_name, GLOBAL_VARS)
    logger.debug(f"\nSTEP AFTER：{new_step_name}\n")
    setattr(step, "name", new_step_name)

    # 获取最新的步骤名称，作为allure的步骤
    step_name = getattr(step, "name", None)
    allure_step(step_title=f"Step_name: --> {step_name}")


# ------------------------------------- END: pytest-bdd钩子函数处理---------------------------------------#

# ------------------------------------- START: pytest钩子函数处理---------------------------------------#
def pytest_runtest_makereport(item, call):
    if call.when == "call":
        # todo 这里并没有生效，留待解决
        if call.excinfo is not None and "page" in item.funcargs:
            page = item.funcargs["page"]
            file_name = f"error_{item.name}_{datetime.now().strftime('%Y-%m-%d %H_%M_%S')}.png"
            page.screenshot(path=os.path.join(IMG_DIR, file_name))


def pytest_terminal_summary(terminalreporter, config):
    """
    收集测试结果
    """
    _RERUN = len([i for i in terminalreporter.stats.get('rerun', []) if i.when != 'teardown'])
    try:
        # 获取pytest传参--reruns的值
        reruns_value = int(config.getoption("--reruns"))
        _RERUN = int(_RERUN / reruns_value)
    except Exception:
        reruns_value = "未配置--reruns参数"
        _RERUN = len([i for i in terminalreporter.stats.get('rerun', []) if i.when != 'teardown'])

    _PASSED = len([i for i in terminalreporter.stats.get('passed', []) if i.when != 'teardown'])
    _ERROR = len([i for i in terminalreporter.stats.get('error', []) if i.when != 'teardown'])
    _FAILED = len([i for i in terminalreporter.stats.get('failed', []) if i.when != 'teardown'])
    _SKIPPED = len([i for i in terminalreporter.stats.get('skipped', []) if i.when != 'teardown'])
    _XPASSED = len([i for i in terminalreporter.stats.get('xpassed', []) if i.when != 'teardown'])
    _XFAILED = len([i for i in terminalreporter.stats.get('xfailed', []) if i.when != 'teardown'])

    _TOTAL = terminalreporter._numcollected

    _DURATION = time.time() - terminalreporter._sessionstarttime

    session_start_time = datetime.fromtimestamp(terminalreporter._sessionstarttime)
    _START_TIME = f"{session_start_time.year}年{session_start_time.month}月{session_start_time.day}日 " \
                  f"{session_start_time.hour}:{session_start_time.minute}:{session_start_time.second}"

    test_info = f"各位同事, 大家好:\n" \
                f"自动化用例于 {_START_TIME}- 开始运行，运行时长：{_DURATION:.2f} s， 目前已执行完成。\n" \
                f"--------------------------------------\n" \
                f"#### 执行结果如下:\n" \
                f"- 用例运行总数: {_TOTAL} 个\n" \
                f"- 跳过用例个数（skipped）: {_SKIPPED} 个\n" \
                f"- 实际执行用例总数: {_PASSED + _FAILED + _XPASSED + _XFAILED} 个\n" \
                f"- 通过用例个数（passed）: {_PASSED} 个\n" \
                f"- 失败用例个数（failed）: {_FAILED} 个\n" \
                f"- 异常用例个数（error）: {_ERROR} 个\n" \
                f"- 重跑的用例数(--reruns的值): {_RERUN} ({reruns_value}) 个\n"
    try:
        _RATE = _PASSED / (_TOTAL - _SKIPPED) * 100
        test_result = f"- 用例成功率: {_RATE:.2f} %\n"
        logger.success(f"{test_info}{test_result}")
        print(test_info, test_result)
    except ZeroDivisionError:
        test_result = "- 用例成功率: 0.00 %\n"
        logger.critical(f"{test_info}{test_result}")
        print(test_info, test_result)

    # 这里是方便在流水线里面发送测试结果到钉钉/企业微信的
    with open(file=os.path.join(REPORT_DIR, "test_result.txt"), mode="w", encoding="utf-8") as f:
        f.write(f"{test_info}{test_result}")

# ------------------------------------- END: pytest钩子函数处理---------------------------------------#
